/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.util;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.iwindplus.boot.domain.constant.CommonConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.ExceptionConstant;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;

/**
 * Kryo工具类.
 *
 * @author zengdegui
 * @since 2018/9/1
 */
@Slf4j
public class KryoUtil {

    private KryoUtil() {
        throw new IllegalStateException(CommonConstant.UTILITY_CLASS);
    }

    private static final ThreadLocal<Kryo> KRYO_THREAD_LOCAL = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        // 支持对象循环引用（否则会栈溢出）
        kryo.setReferences(true);
        // 不强制要求注册类（注册行为无法保证多个 JVM 内同一个类的注册编号相同；而且业务系统中大量的 Class 也难以一一注册）
        kryo.setRegistrationRequired(false);
        return kryo;
    });

    /**
     * 获得当前线程的 Kryo 实例.
     *
     * @return 当前线程的 Kryo 实例
     */
    public static Kryo getInstance() {
        return KRYO_THREAD_LOCAL.get();
    }

    /**
     * 清除当前线程的 Kryo 实例
     */
    public static void remove() {
        if (Objects.nonNull(KRYO_THREAD_LOCAL.get())) {
            KRYO_THREAD_LOCAL.remove();
        }
    }

    /**
     * 序列化为字节数组.
     *
     * @param obj 任意对象
     * @param <T> 对象的类型
     * @return byte[]
     */
    public static <T> byte[] toJsonBytes(T obj) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            Output output = new Output(byteArrayOutputStream)) {
            Kryo kryo = KryoUtil.getInstance();
            kryo.writeClassAndObject(output, obj);
            output.close();
            return byteArrayOutputStream.toByteArray();
        } catch (Exception ex) {
            log.error(ExceptionConstant.EXCEPTION, ex);
        } finally {
            KryoUtil.remove();
        }
        return new byte[0];
    }

    /**
     * 序列化为String（Base64编码）.
     *
     * @param obj 任意对象
     * @param <T> 对象的类型
     * @return byte[]
     */
    public static <T> String toJsonStr(T obj) {
        final byte[] bytes = KryoUtil.toJsonBytes(obj);
        return Base64Encoder.encode(bytes);
    }

    /**
     * 反序列化为对象.
     *
     * @param bytes 字节数组
     * @param <T>   泛型
     * @return T
     */
    public static <T> T parseBytes(byte[] bytes) {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            Input input = new Input(byteArrayInputStream)) {
            Kryo kryo = KryoUtil.getInstance();
            final Object obj = kryo.readClassAndObject(input);
            input.close();
            return (T) obj;
        } catch (Exception ex) {
            log.error(ExceptionConstant.EXCEPTION, ex);
        } finally {
            KryoUtil.remove();
        }
        return null;
    }

    /**
     * 反序列化为对象（Base64编码）.
     *
     * @param str 序列化后的字符串
     * @param <T> 泛型
     * @return T
     */
    public static <T> T parseObject(String str) {
        final byte[] bytes = Base64Decoder.decode(str);
        return KryoUtil.parseBytes(bytes);
    }

}